// Code file original author: Martin Lapierre, http://devinstinct.com

using System;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using EntLibContrib.Validation.Validators;

namespace EntLibContrib.Validation
{
    /// <summary>
    /// Represents an argument validation class to 
    /// test method pre-conditions and post-conditions.
    /// </summary>
    public static class ArgumentValidation
    {
        #region Methods

        /// <summary>
        /// Validates an argument using the provided validators and throws 
        /// an <see cref="ArgumentValidationException"/> containing the parameter value if the validation fails.
        /// </summary>
        /// <param name="paramName">The name of the argument.</param>
        /// <param name="param">The argument.</param>
        /// <param name="validators">The validators to use to test the argument.</param>
        public static void Validate(string paramName, object param, params Validator[] validators)
        {
            // Validate pre-conditions.
            // Done in overlead.

            Validate(null, paramName, param, false, validators);
        }

        /// <summary>
        /// Validates an argument using the provided validators and throws 
        /// an <see cref="ArgumentValidationException"/> optionally containing the parameter value if the validation fails.
        /// </summary>
        /// <param name="paramName">The name of the argument.</param>
        /// <param name="param">The argument.</param>
        /// <param name="safeException">True if the parameter value shouldn't be attached to the exception, false otherwise.</param>
        /// <param name="validators">The validators to use to test the argument.</param>
        public static void Validate(string paramName, object param, bool safeException, params Validator[] validators)
        {
            // Validate pre-conditions.
            // Done in overlead.

            Validate(null, paramName, param, safeException, validators);
        }

        /// <summary>
        /// Validates an argument using the provided validators and throws 
        /// an <see cref="ArgumentValidationException"/> optionally containing the parameter value if the validation fails.
        /// </summary>
        /// <param name="message">The error message that explains the reason for the exception.</param>
        /// <param name="paramName">The name of the argument.</param>
        /// <param name="param">The argument.</param>
        /// <param name="safeException">True if the parameter value shouldn't be attached to the exception, false otherwise.</param>
        /// <param name="validators">The validators to use to test the argument.</param>
        public static void Validate(string message, string paramName, object param, bool safeException, params Validator[] validators)
        {
            // Validate pre-conditions.
            // "message" can be null.
            // "param" can be null.
            InnerValidate(null, "paramName", paramName, false, DefaultValidators.StringNotNullNorEmptyValidator);
            InnerValidate(null, "validators", validators, false, DefaultValidators.CollectionNotNullNorEmptyValidator);

            // Validate the provided parameter.
            InnerValidate(message, paramName, param, safeException, validators);
        }

        /// <summary>
        /// Contains the validation process logic.
        /// </summary>
        /// <param name="message">The error message that explains the reason for the exception.</param>
        /// <param name="paramName">The name of the argument.</param>
        /// <param name="param">The argument.</param>
        /// <param name="safeException">True if the parameter value shouldn't be attached to the exception, false otherwise.</param>
        /// <param name="validators">The validators to use to test the argument.</param>
        private static void InnerValidate(string message, string paramName, object param, bool safeException, params Validator[] validators)
        {
            // Validate pre-conditions.
            // Internal class call: arguments assumed to be valid.
            // "message" can be null.
            // "param" can be null.

            Validator mainValidator = null;
            ValidationResults validationResults = null;

            if (validators.Length == 1)
                mainValidator = validators[0];
            else
                mainValidator = new AndCompositeValidator(validators);

            validationResults = mainValidator.Validate(param);
            if (!validationResults.IsValid)
            {
                if (message == null)
                    message = Resources.ArgumentValidationDefaultMessage;

                if (safeException) // Do not propagate parameter in exception.
                    throw new ArgumentValidationException(message, paramName, validationResults);
                else // Propagate parameter in exception.
                    throw new ArgumentValidationException(message, paramName, param, validationResults);
            }
        }

        #endregion Methods
    }
}
